#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>

#include "udp-one.h"

#ifndef G_OS_WIN32
#include <unistd.h>
#include <nice/nice.h>

#endif

//#include "agent-priv.h"
//#define DBG_RAW(LV, FMT, ARGS...) nice_debug("|%7s|" LV "| " FMT, "udp-one", ##ARGS);

//#define DBG_RAW(LV, FMT, ARGS...) \
//    do{  printf("|%7s|" LV "| " FMT, "udp-one", ##ARGS); printf("\n"); fflush(stdout); }while(0)

#define DBG_RAW(LV, FMT, ARGS...)

#define dbgd(FMT, ARGS...) DBG_RAW("D", FMT, ##ARGS)
#define dbgi(FMT, ARGS...) DBG_RAW("I", FMT, ##ARGS)
#define dbge(FMT, ARGS...) DBG_RAW("E", FMT, ##ARGS)

#define odbgd(priv, FMT, ARGS...) dbgd("%p| " FMT, priv, ##ARGS);

typedef struct nativeaddr{
    union {
        struct sockaddr_storage storage;
        struct sockaddr addr;
    } s;
}nativeaddr;


typedef struct{
    union
    {
        struct sockaddr     addr;
        struct sockaddr_in  ip4;
        struct sockaddr_in6 ip6;
    } s;
}ysockaddr;
#define YSOCK_MAX_ADDR_STR_SIZE (INET6_ADDRSTRLEN+8)




gboolean ysockaddr_equal(const ysockaddr* a, const ysockaddr* b);
guint ysockaddr_hash(const ysockaddr* a);
void ysockaddr_from_sockaddr (ysockaddr *addr, const struct sockaddr *sa);
guint ysockaddr_get_port (const ysockaddr *addr);
char * ysockaddr_to_ipstring (const ysockaddr *addr, gchar *dst);
char * ysockaddr_to_string (const ysockaddr *addr, gchar *dst);



static guint ipv6_hash_value(const ysockaddr* a){
    guint32 * p0 = (guint32 *)&a->s.ip6.sin6_addr;
    guint32 * p1 = p0+1;
    guint32 * p2 = p0+2;
    guint32 * p3 = p0+3;
    return a->s.ip6.sin6_port*9999 + a->s.ip6.sin6_scope_id*999 + *p0 + *p1 + *p2 + *p3;
}

gboolean ysockaddr_equal(const ysockaddr* a, const ysockaddr* b){
    if (a->s.addr.sa_family != b->s.addr.sa_family)
        return FALSE;
    
    switch (a->s.addr.sa_family)
    {
        case AF_INET:
            return (a->s.ip4.sin_addr.s_addr == b->s.ip4.sin_addr.s_addr)
            && (a->s.ip4.sin_port == b->s.ip4.sin_port);
            
        case AF_INET6:
            return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr)
            && (a->s.ip6.sin6_port == b->s.ip6.sin6_port)
            && (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id);
            
        default:
            g_return_val_if_reached (FALSE);
    }
}

guint ysockaddr_hash(const ysockaddr* a){
    switch (a->s.addr.sa_family)
    {
        case AF_INET6:
            return ipv6_hash_value(a);
            
        case AF_INET:
            return a->s.ip4.sin_port*9999 + a->s.ip4.sin_addr.s_addr;
            
        default:
            return a->s.ip4.sin_port*9999 + a->s.ip4.sin_addr.s_addr;
    }
}

void ysockaddr_from_sockaddr (ysockaddr *addr, const struct sockaddr *sa){
    switch (sa->sa_family){
        case AF_INET:
            memcpy(&addr->s.ip4, sa, sizeof (addr->s.ip4));
            break;
        case AF_INET6:
            memcpy(&addr->s.ip6, sa, sizeof (addr->s.ip6));
            break;
        default:
            g_return_if_reached ();
    }
}

char * ysockaddr_to_ipstring (const ysockaddr *addr, gchar *dst) {
    dst[0]='\0';
    switch (addr->s.addr.sa_family) {
        case AF_INET:
            inet_ntop (AF_INET, &addr->s.ip4.sin_addr, dst, INET_ADDRSTRLEN);
            break;
        case AF_INET6:
            inet_ntop (AF_INET6, &addr->s.ip6.sin6_addr, dst, INET6_ADDRSTRLEN);
            break;
        default:
            break;
    }
    return dst;
}

char * ysockaddr_to_string (const ysockaddr *addr, gchar *dst) {
    gchar tmp[YSOCK_MAX_ADDR_STR_SIZE];
    ysockaddr_to_ipstring(addr, tmp);
    sprintf(dst, "%s:%d", tmp, ysockaddr_get_port(addr));
    return dst;
}

guint ysockaddr_get_port (const ysockaddr *addr){
    if (!addr){
        return 0;
    }
    
    switch (addr->s.addr.sa_family){
        case AF_INET:
            return ntohs (addr->s.ip4.sin_port);
        case AF_INET6:
            return ntohs (addr->s.ip6.sin6_port);
        default:
            g_return_val_if_reached (0);
    }
}



typedef struct{
    char * data;
    size_t size;
}ystr_t;



typedef struct{
    uint16_t msgtype;
    uint16_t length; // payload length, don't include header
    uint32_t cookie; // rfc 5398 magic cookie
    uint8_t tid[12]; // transaction id
    ystr_t username;
}ystun_t;

#define YALGIN_4BYTES(v) (((v)+3) / 4 * 4)

#define YSTUN_MAGIC_COOKIE        0x2112A442  // 554869826
#define YSTUN_MESSAGE_CLASS(type)            ((((type) >> 7) & 0x02) | (((type) >> 4) & 0x01))
#define YSTUN_MESSAGE_METHOD(type)            ((((type) >> 2) & 0x0F80) | (((type) >> 1) & 0x0070) | ((type) & 0x000F))

enum YSTUN_MESSAGE_CLASS_TYPE{
    YSTUN_CLASS_REQUEST = 0,
    YSTUN_CLASS_INDICATION = 1,
    YSTUN_CLASS_RESPONSE_OK = 2,
    YSTUN_CLASS_RESPONSE_ERR = 3
};

enum YSTUN_ATTR_TYPE{
    YSTUN_ATTR_USERNAME                  = 0x0006,
};


const char * ystun_class_name(uint16_t clazz);
int ystun_parse(uint8_t *data, size_t size, ystun_t * msg);
size_t ystun_first_ufrag(const ystun_t * msg, ystr_t * dst);
size_t ystun_second_ufrag(const ystun_t * msg, ystr_t * dst);
void yaddr_from_nice_addr(ysockaddr * yaddr, const NiceAddress * naddr);
void yaddr_to_nice_addr(const ysockaddr * yaddr, NiceAddress * naddr);

static inline void be_read_uint16(const uint8_t* ptr, uint16_t* val)
{
    *val = (((uint16_t)ptr[0]) << 8) | ptr[1];
}

//static inline void be_read_uint24(const uint8_t* ptr, uint32_t* val)
//{
//    *val = (((uint32_t)ptr[0]) << 16) | (((uint32_t)ptr[1]) << 8) | ptr[2];
//}

static inline void be_read_uint32(const uint8_t* ptr, uint32_t* val)
{
    *val = (((uint32_t)ptr[0]) << 24) | (((uint32_t)ptr[1]) << 16) | (((uint32_t)ptr[2]) << 8) | ptr[3];
}

const char * ystun_class_name(uint16_t clazz){
    // A class of 0b00 is a request,
    // a class of 0b01 is an indication,
    // a class of 0b10 is a success response,
    // and a class of 0b11 is an error response.
    if(clazz == YSTUN_CLASS_REQUEST){
        return "class-req";
    }else if(clazz == YSTUN_CLASS_INDICATION){
        return "class-indi";
    }else if(clazz == YSTUN_CLASS_RESPONSE_OK){
        return "class-rspok";
    }else if(clazz == YSTUN_CLASS_RESPONSE_ERR){
        return "class-rsperr";
    }else {
        return "class-unknown";
    }
}

int ystun_parse(uint8_t *data, size_t size, ystun_t * msg){
    if (!data || size < 20){
        return -(__LINE__);
    }
    
    // header
    be_read_uint16(data, &msg->msgtype);
    if((msg->msgtype & 0xC000) != 0){
        // The most significant 2 bits of every STUN message MUST be zeroes
        return -(__LINE__);
    }
    
    be_read_uint16(data + 2, &msg->length);
    if((msg->length & 0x0003) != 0){
        // Since all STUN attributes are padded to a multiple of 4 bytes,
        // the last 2 bits of this field are always zero.
        return -(__LINE__);
    }
    if(msg->length < 4 || msg->length > (size-20)){
        return -(__LINE__);
    }
    
    be_read_uint32(data + 4, &msg->cookie);
    if(msg->cookie != YSTUN_MAGIC_COOKIE){
        return -(__LINE__);
    }
    
    memcpy(msg->tid, data + 8, 12);
    
    uint16_t attr_type;
    uint16_t attr_len;
    
    for (int pos = 0; pos <= (msg->length-4); ){
        uint8_t * p = data+20+pos;
        be_read_uint16(p, &attr_type);
        be_read_uint16(p + 2, &attr_len);
        
        if ((pos + 4 + attr_len) > msg->length){
            return -(__LINE__);
        }
        
        if(attr_type == YSTUN_ATTR_USERNAME){
            msg->username.data = (char *)(p+4);
            msg->username.size = attr_len;
            return 0;
        }
        pos += 4 + YALGIN_4BYTES(attr_len);
    }
    return -(__LINE__);
}

size_t ystun_first_ufrag(const ystun_t * msg, ystr_t * dst){
    const ystr_t * src = &msg->username;
    dst->data = src->data;
    for(size_t i = 0; i < src->size; ++i){
        if(src->data[i] == ':'){
            dst->size = i;
            return dst->size;
        }
    }
    dst->size = 0;
    return dst->size;
}

size_t ystun_second_ufrag(const ystun_t * msg, ystr_t * dst){
    const ystr_t * src = &msg->username;
    
    for(size_t i = 0; i < src->size; ++i){
        if(src->data[i] == ':'){
            if(i == (src->size-1)){
                break;
            }
            dst->data = src->data + i + 1;
            dst->size = src->size-i-1;
            return dst->size;
        }
    }
    dst->size = 0;
    return dst->size;
}

void yaddr_from_nice_addr(ysockaddr * yaddr, const NiceAddress * naddr){
    if(naddr->s.addr.sa_family == AF_INET){
        yaddr->s.ip4 = naddr->s.ip4;
    }else if(naddr->s.addr.sa_family == AF_INET6){
        yaddr->s.ip6 = naddr->s.ip6;
    }else{
        yaddr->s.addr = naddr->s.addr;
    }
}

void yaddr_to_nice_addr(const ysockaddr * yaddr, NiceAddress * naddr){
    if(yaddr->s.addr.sa_family == AF_INET){
        naddr->s.ip4 = yaddr->s.ip4;
    }else if(yaddr->s.addr.sa_family == AF_INET6){
        naddr->s.ip6 = yaddr->s.ip6;
    }else{
        naddr->s.addr = yaddr->s.addr;
    }
}

void print_recv_udp(NiceInputMessage *recv_message, gssize recv_size, GSocketAddress * gaddr){
    if(recv_size <= 0 || !gaddr || recv_message->n_buffers != 1){
        //dbgd("udp msg: skip data, size=%zd, gaddr=%p, nbuf=%d",
        //       recv_size, gaddr, recv_message->n_buffers);
        return;
    }
    
    uint8_t * data = recv_message->buffers[0].buffer;
    ystun_t msg;
    int ret = ystun_parse(data, recv_size, &msg);
    if(ret == 0){
        gchar tmpbuf[YSOCK_MAX_ADDR_STR_SIZE];
        ysockaddr yaddr;

        nativeaddr sa;
        
        g_socket_address_to_native (gaddr, &sa.s.addr, sizeof (sa.s), NULL);
        //nice_address_set_from_sockaddr (recv_message->from, &sa.addr);
        ysockaddr_from_sockaddr(&yaddr, &sa.s.addr);
        
        dbgd("udp msg: stun, username=[%.*s], class=[%s], from=[%s]",
             (int)msg.username.size, msg.username.data,
             ystun_class_name(YSTUN_MESSAGE_CLASS(msg.msgtype)),
             ysockaddr_to_string(&yaddr, tmpbuf));
    }else{
        dbgd("udp msg: raw, ret=%d, data[0]=%02X, length=%zu", ret, data[0], recv_size);
    }
}

#define MAX_REAL_BUF_SIZE 2048
#define YSTUN_MAX_UFRAG   256 + 1  /* ufrag + NULL */
#define YSTUN_MAX_UNAME   256 * 2 + 1 + 1 /* 2*ufrag + colon + NULL */

typedef struct RealUdpSocket{
    ysockaddr local_yaddr;
    GSocket *gsock;
    GSource * source;
    uint32_t num4privs;
    
    GHashTable * addr_map;  // addr -> priv; n to 1
    GHashTable * ufrag_map; // ufrag -> priv; 1 to 1
    gchar username_buf[YSTUN_MAX_UNAME];
    
    guint8 recv_buf[MAX_REAL_BUF_SIZE];
    gssize recv_length;
    gint recv_flags;
    nativeaddr recv_addr;
}RealUdpSocket;

typedef struct UdpOneSocketPrivate{
    NiceAddress niceaddr; // remote addr
    GSocketAddress *gaddr;
    RealUdpSocket * rsock;
    GSocketSourceFunc on_readable_func;
    gpointer on_readable_user_data;
    gchar * local_ufrag;
    void * stream;
    guint component_id;
    GSList * map_yaddr_list;
}UdpOneSocketPrivate;

typedef struct RealSocketManager{
    GHashTable * rsock_map;
}RealSocketManager;




RealUdpSocket * realudp_new(NiceAddress *addr);
void realudp_free(RealUdpSocket * rsock);
gboolean realudp_remove_ufrag_map(RealUdpSocket * rsock, UdpOneSocketPrivate * priv);
void realudp_insert_ufrag_map(RealUdpSocket * rsock, UdpOneSocketPrivate * priv);
RealSocketManager * manager_inst(void);
RealUdpSocket * manager_get_or_new_rsock(RealSocketManager * mgr, NiceAddress *addr);


static GSocket * new_gsock_udp(NiceAddress *addr, ysockaddr * realaddr){
    gboolean gret = FALSE;
    GSocketAddress *gaddr = NULL;
    
    GSocket *gsock = NULL;
    nativeaddr name;
    
    if (addr != NULL) {
        nice_address_copy_to_sockaddr(addr, &name.s.addr);
    } else {
        memset (&name, 0, sizeof (name));
        name.s.storage.ss_family = AF_UNSPEC;
    }
    
    if (name.s.storage.ss_family == AF_UNSPEC || name.s.storage.ss_family == AF_INET) {
        gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
                              G_SOCKET_PROTOCOL_UDP, NULL);
        name.s.storage.ss_family = AF_INET;
#ifdef HAVE_SA_LEN
        name.s.storage.ss_len = sizeof (struct sockaddr_in);
#endif
    } else if (name.s.storage.ss_family == AF_INET6) {
        gsock = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_DATAGRAM,
                              G_SOCKET_PROTOCOL_UDP, NULL);
        name.s.storage.ss_family = AF_INET6;
#ifdef HAVE_SA_LEN
        name.s.storage.ss_len = sizeof (struct sockaddr_in6);
#endif
    }
    
    /* GSocket: All socket file descriptors are set to be close-on-exec. */
    g_socket_set_blocking (gsock, false);

    gaddr = g_socket_address_new_from_native (&name.s.addr, sizeof (name.s));
    if (gaddr != NULL) {
        gret = g_socket_bind (gsock, gaddr, FALSE, NULL);
        g_object_unref (gaddr);
    }
    
    if (gret == FALSE) {
        g_socket_close (gsock, NULL);
        g_object_unref (gsock);
        return NULL;
    }

    gaddr = g_socket_get_local_address (gsock, NULL);
    if (gaddr == NULL || !g_socket_address_to_native (gaddr, &name.s.addr, sizeof(name.s), NULL)) {
        g_socket_close (gsock, NULL);
        g_object_unref (gsock);
        return NULL;
    }
    
    g_object_unref (gaddr);
    
    ysockaddr_from_sockaddr (realaddr, &name.s.addr);
    
    return gsock;
}

static
void yaddr_free(gpointer data){
    if(data){
        g_slice_free(ysockaddr, (ysockaddr *)data);
    }
}

static
gboolean on_readable_event (GSocket *socket,
                            GIOCondition condition,
                            gpointer user_data){
    RealUdpSocket* obj = (RealUdpSocket*)(user_data);
    if(condition & G_IO_IN){
        ysockaddr yaddr;
        while(1){
            GInputVector local_bufs = { obj->recv_buf, MAX_REAL_BUF_SIZE };
            GError *gerr = NULL;
            GSocketAddress * gaddr = NULL;
            obj->recv_flags = G_SOCKET_MSG_NONE;
            obj->recv_length = g_socket_receive_message (obj->gsock,
                                                         &gaddr,
                                                         &local_bufs, 1, NULL, NULL,
                                                         &obj->recv_flags, NULL, &gerr);
            obj->recv_length = MAX (obj->recv_length, 0);
            
            if(gaddr){
                g_socket_address_to_native (gaddr, &obj->recv_addr.s.addr, sizeof (obj->recv_addr.s), NULL);
                g_object_unref (gaddr);
                gaddr = NULL;
                ysockaddr_from_sockaddr(&yaddr, &obj->recv_addr.s.addr);
            }
            
            
            if (obj->recv_length <= 0) {
                if(gerr){
                    if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)){
                        obj->recv_length = 0;
                    }
                    g_error_free (gerr);
                }
                
                break;
            }
            
            
            UdpOneSocketPrivate *priv = g_hash_table_lookup(obj->addr_map, &yaddr);
            if(!priv){
                ystun_t msg;
                int parsed_ret = ystun_parse(obj->recv_buf, obj->recv_length, &msg);
                if(parsed_ret < 0){
                    dbgd("parse stun msg fail with [%d]", parsed_ret);
                    continue;
                }
                
                ystr_t ufrag;
                uint16_t stun_class = YSTUN_MESSAGE_CLASS(msg.msgtype);
                if(stun_class == YSTUN_CLASS_REQUEST){
                    ystun_first_ufrag(&msg, &ufrag);
                }else if(stun_class == YSTUN_CLASS_RESPONSE_OK
                         || stun_class == YSTUN_CLASS_RESPONSE_ERR){
                    ystun_second_ufrag(&msg, &ufrag);
                }else{
                    dbgd("unknown stun class [%u]", stun_class);
                    continue;
                }
                
                if(ufrag.size == 0 || ufrag.size > (YSTUN_MAX_UNAME-1)){
                    dbgd("wrong ufrag size [%zu]", ufrag.size);
                    continue;
                }
                memcpy(obj->username_buf, ufrag.data, ufrag.size);
                obj->username_buf[ufrag.size] = '\0';
                priv = g_hash_table_lookup(obj->ufrag_map, obj->username_buf);
                if(!priv){
                    odbgd(priv, "NOT found ufrag map [%s], stun=[%s]", obj->username_buf, ystun_class_name(stun_class));
                    continue;
                }
                odbgd(priv, "did found ufrag map [%s], stun=[%s]", obj->username_buf, ystun_class_name(stun_class));
                
                ysockaddr *yaddr_copied = g_slice_dup(ysockaddr, &yaddr);
                priv->map_yaddr_list = g_slist_prepend (priv->map_yaddr_list, yaddr_copied);
                g_hash_table_insert(obj->addr_map, yaddr_copied, priv);
                
                gchar tmpstr[YSOCK_MAX_ADDR_STR_SIZE];
                ysockaddr_to_string(yaddr_copied, tmpstr);
                odbgd(priv, "add addr map [%s]", tmpstr);
            }
            
            if(priv->on_readable_func){
                priv->on_readable_func(socket, G_IO_IN, priv->on_readable_user_data);
            }
        }
    }

    return G_SOURCE_CONTINUE;
}


RealUdpSocket * realudp_new(NiceAddress *addr){

    RealUdpSocket * obj = g_slice_new0 (RealUdpSocket);
    obj->gsock = new_gsock_udp(addr, &obj->local_yaddr);
    if(!obj->gsock){
        g_slice_free (RealUdpSocket, obj);
        return NULL;
    }
    
    obj->source = g_socket_create_source (obj->gsock, G_IO_IN, NULL);
    g_source_set_callback(obj->source, (GSourceFunc)on_readable_event, obj, NULL);
    g_source_attach(obj->source, NULL);
    
    obj->addr_map = g_hash_table_new_full ((GHashFunc)ysockaddr_hash,
                                           (GEqualFunc)ysockaddr_equal,
                                           NULL,
                                           NULL);
    
    obj->ufrag_map = g_hash_table_new_full ((GHashFunc)g_str_hash,
                                            (GEqualFunc)g_str_equal,
                                            NULL,
                                            NULL);
    
    return obj;
}

void realudp_free(RealUdpSocket * rsock){
    if(!rsock){
        return;
    }
    
    if(rsock->source){
        g_source_destroy(rsock->source);
        g_source_unref(rsock->source);
        rsock->source = NULL;
    }
    
    if(rsock->gsock){
        g_socket_close (rsock->gsock, NULL);
        g_object_unref (rsock->gsock);
        rsock->gsock = NULL;
    }
    
    if(rsock->addr_map){
        g_hash_table_unref(rsock->addr_map);
        rsock->addr_map = NULL;
    }
    
    if(rsock->ufrag_map){
        g_hash_table_unref(rsock->ufrag_map);
        rsock->ufrag_map = NULL;
    }

    g_slice_free(RealUdpSocket, rsock);
}

gboolean realudp_remove_ufrag_map(RealUdpSocket * rsock, UdpOneSocketPrivate * priv){
    if(priv->local_ufrag){
        UdpOneSocketPrivate * exist = g_hash_table_lookup(rsock->ufrag_map, priv->local_ufrag);
        if(exist == priv){
            odbgd(priv, "remove ufrag map [%s]", priv->local_ufrag);
            g_hash_table_remove(rsock->ufrag_map, priv->local_ufrag);
            return TRUE;
        }
    }
    return FALSE;
}

void realudp_insert_ufrag_map(RealUdpSocket * rsock, UdpOneSocketPrivate * priv){
    if(priv->local_ufrag){
        UdpOneSocketPrivate * exist = g_hash_table_lookup(rsock->ufrag_map, priv->local_ufrag);
        if(exist){
            if(exist == priv){
                return;
            }
            g_hash_table_remove(rsock->ufrag_map, priv->local_ufrag);
            g_free(priv->local_ufrag);
            priv->local_ufrag = NULL;
        }
        g_hash_table_insert(rsock->ufrag_map, priv->local_ufrag, priv);
    }
}

RealSocketManager * manager_inst(void){
    static RealSocketManager * mgr = NULL;
    if(!mgr){
        mgr = g_slice_new0(RealSocketManager);
        mgr->rsock_map = g_hash_table_new_full ((GHashFunc)ysockaddr_hash,
                                                (GEqualFunc)ysockaddr_equal,
                                                NULL,
                                                (GDestroyNotify)realudp_free);
    }
    return mgr;
}


RealUdpSocket * manager_get_or_new_rsock(RealSocketManager * mgr, NiceAddress *addr){
    ysockaddr yaddr;
    yaddr_from_nice_addr(&yaddr, addr);
    
    RealUdpSocket * rsock = (RealUdpSocket *)g_hash_table_lookup(mgr->rsock_map, &yaddr);
    if(rsock){
        // exist real socket, return it
        return rsock;
    }
    
    rsock = realudp_new(addr);
    if(!rsock){
        return NULL;
    }
    
    g_hash_table_insert(mgr->rsock_map, &rsock->local_yaddr, rsock);
    
    return rsock;
}


static
gint op_send_messages(NiceSocket *nsock,
                      const NiceAddress *to,
                      const NiceOutputMessage *messages,
                      guint n_messages) {
    
    if (nsock->priv == NULL) {
        return -1;
    }
    
    GOutputMessage output_msgs[IOV_MAX];
    struct UdpOneSocketPrivate *priv = nsock->priv;
    GError *error = NULL;
    
    memset(output_msgs, 0, sizeof(output_msgs));
    
    if (!nice_address_is_valid (&priv->niceaddr) ||
        !nice_address_equal (&priv->niceaddr, to)) {
        union {
            struct sockaddr_storage storage;
            struct sockaddr addr;
        } sa;
        GSocketAddress *gaddr;
        
        if (priv->gaddr){
            g_object_unref (priv->gaddr);
        }
        
        nice_address_copy_to_sockaddr (to, &sa.addr);
        gaddr = g_socket_address_new_from_native (&sa.addr, sizeof(sa));
        priv->gaddr = gaddr;
        
        if (gaddr == NULL){
            return -(__LINE__);
        }
        
        priv->niceaddr = *to;
        
        gchar tmpbuf[YSOCK_MAX_ADDR_STR_SIZE];
        nice_address_to_string(&priv->niceaddr, tmpbuf);
        odbgd(priv, "change dst-addr=[%s:%d]",
              tmpbuf, nice_address_get_port(&priv->niceaddr));
    }
    
    guint i;
    for (i = 0; i < n_messages; i ++) {
        output_msgs[i].address = priv->gaddr;
        output_msgs[i].vectors = messages[i].buffers;
        output_msgs[i].num_vectors = messages[i].n_buffers;
        output_msgs[i].control_messages = NULL;
        output_msgs[i].num_control_messages = 0;
    }
    
    gint num_msgs_send = g_socket_send_messages(priv->rsock->gsock, output_msgs, n_messages,
                                                G_SOCKET_MSG_NONE, NULL, &error);
    return num_msgs_send;
}

static
gint op_recv_messages (NiceSocket *nsock,
                       NiceInputMessage *recv_messages,
                       guint n_recv_messages){
    struct UdpOneSocketPrivate *priv = nsock->priv;
    if(!priv){
        return -1;
    }
    
    if(priv->rsock->recv_length < 1
       || n_recv_messages < 1
       || recv_messages[0].n_buffers < 1){
        return 0;
    }
    
    gssize remains = priv->rsock->recv_length;
    for(gint i = 0; i < recv_messages[0].n_buffers && remains > 0; ++i){
        GInputVector * inputvec = &recv_messages[0].buffers[i];
        gssize buf_siz = inputvec->size;
        gssize bytes = remains < buf_siz ? remains : buf_siz ;
        if(bytes > 0){
            memcpy(inputvec->buffer,
                   priv->rsock->recv_buf + priv->rsock->recv_length - remains,
                   bytes);
        }
        remains -= bytes;
    }
    
    if(remains > 0){
        return -1;
    }

    recv_messages->length = priv->rsock->recv_length;
    priv->rsock->recv_length = 0;
    
    if(recv_messages->from){
        nice_address_set_from_sockaddr (recv_messages->from, &priv->rsock->recv_addr.s.addr);
    }
    return 1; // return one message
}

static
void op_close (NiceSocket *nsock){
    struct UdpOneSocketPrivate *priv = nsock->priv;
    
    realudp_remove_ufrag_map(priv->rsock, priv);
    
    --priv->rsock->num4privs;
    
    if(priv->map_yaddr_list){
        for (GSList * item = priv->map_yaddr_list; item; item = g_slist_next (item)) {
            ysockaddr * yaddr = item->data;
            UdpOneSocketPrivate * exist = g_hash_table_lookup(priv->rsock->addr_map, yaddr);
            if(exist == priv){
                gchar tmpstr[YSOCK_MAX_ADDR_STR_SIZE];
                ysockaddr_to_string(yaddr, tmpstr);
                odbgd(priv, "remove addr map [%s]", tmpstr);
                g_hash_table_remove(priv->rsock->addr_map, yaddr);
            }
            yaddr_free(yaddr);
        }
        g_slist_free(priv->map_yaddr_list);
        priv->map_yaddr_list = NULL;
    }
    
    if (priv->gaddr){
        g_object_unref (priv->gaddr);
    }
    
    if(priv->local_ufrag){
        g_free(priv->local_ufrag);
        priv->local_ufrag = NULL;
    }
    
    g_slice_free (struct UdpOneSocketPrivate, nsock->priv);
    nsock->priv = NULL;
}

static
void op_set_readable_callback (NiceSocket *nsock,
                               GSocketSourceFunc func,
                               gpointer user_data,
                               const gchar * local_ufrag,
                               void * stream,
                               guint component_id){
    UdpOneSocketPrivate *priv = nsock->priv;
    priv->on_readable_func = func;
    priv->on_readable_user_data = user_data;
    priv->stream = stream;
    priv->component_id = component_id;
    
    UdpOneSocketPrivate * exist = g_hash_table_lookup(priv->rsock->ufrag_map, local_ufrag);
    if(exist && exist != priv){
        if((exist->stream == priv->stream)
           && (exist->component_id <= priv->component_id)){
            odbgd(priv, "exist ufrag map [%s]->[%p-%p-%u], ignore [%p-%p-%u]"
                  , local_ufrag
                  , exist, exist->stream, exist->component_id
                  , priv,  priv->stream,  priv->component_id);
            return;
        }
        odbgd(priv, "exist ufrag map [%s]->[%p-%p-%u], override by [%p-%p-%u]"
              , local_ufrag
              , exist, exist->stream, exist->component_id
              , priv,  priv->stream,  priv->component_id);
        return;
    }
    
    realudp_remove_ufrag_map(priv->rsock, priv);
    if(priv->local_ufrag){
        odbgd(priv, "update ufrag map [%s]", priv->local_ufrag);
        g_free(priv->local_ufrag);
        priv->local_ufrag = NULL;
    }else{
        odbgd(priv, "add ufrag map[%s]", local_ufrag);
    }
    priv->local_ufrag = g_strdup (local_ufrag);
    realudp_insert_ufrag_map(priv->rsock, priv);
}

static
gint op_send_messages_reliable_dummy (NiceSocket *sock,
                                      const NiceAddress *to,
                                      const NiceOutputMessage *messages,
                                      guint n_messages){
    return -1;
}

static
gboolean op_is_reliable_dummy (NiceSocket *sock){
    return FALSE;
}

static
gboolean op_can_send_dummy (NiceSocket *sock, NiceAddress *addr){
    return TRUE;
}

static
void op_set_writable_callback_dummy (NiceSocket *sock,
                                     NiceSocketWritableCb callback,
                                     gpointer user_data){
}

NiceSocket * socket_new_test (NiceAddress *addr){
    NiceSocket * nsock = g_slice_new0 (NiceSocket);
    return nsock;
}

NiceSocket * nice_udp_one_socket_new (NiceAddress *addr){
    RealSocketManager * mgr = manager_inst();
    RealUdpSocket * rsock = manager_get_or_new_rsock(mgr, addr);
    if(!rsock){
        gchar tmpbuf[YSOCK_MAX_ADDR_STR_SIZE];
        nice_address_to_string(addr, tmpbuf);
        dbge("can't bind socket at addr=[%s:%d]", tmpbuf, nice_address_get_port(addr));
        return NULL;
    }
    
    
    NiceSocket * nsock = g_slice_new0 (NiceSocket);
    UdpOneSocketPrivate *priv = g_slice_new0 (struct UdpOneSocketPrivate);
    nice_address_init (&priv->niceaddr);
    priv->rsock = rsock;
    ++rsock->num4privs;
    
    gchar tmpbuf[YSOCK_MAX_ADDR_STR_SIZE];
    ysockaddr_to_string(&rsock->local_yaddr, tmpbuf);
    odbgd(priv, "new-one: rsock num-privs=[%u], addr=[%s]",
          rsock->num4privs, tmpbuf);
    
    nsock->priv = priv;
    yaddr_to_nice_addr(&rsock->local_yaddr, &nsock->addr);
    
    nsock->type = NICE_SOCKET_TYPE_UDP_BSD;
    nsock->fileno = rsock->gsock;
    nsock->send_messages = op_send_messages;
    nsock->recv_messages = op_recv_messages;
    nsock->close = op_close;
    nsock->send_messages_reliable = op_send_messages_reliable_dummy;
    nsock->is_reliable = op_is_reliable_dummy;
    nsock->can_send = op_can_send_dummy;
    nsock->set_writable_callback = op_set_writable_callback_dummy;
    nsock->set_readable_callback = op_set_readable_callback;
    return nsock;

}

